/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package com.flexengine.scene;

import com.flexengine.engine.Finalizable;
import com.flexengine.engine.Initializable;
import com.flexengine.engine.Priority;
import com.flexengine.engine.Renderable;
import com.flexengine.engine.Updatable;
import com.flexengine.logger.Logger;
import com.flexengine.map.GameMap;
import com.flexengine.view.GameWindow;
import com.flexengine.view.Graphics;
import java.util.ArrayList;
import java.util.List;

/**
 * @author Luann R. Athayde
 * @version 1.0
 * @since 1.0
 */
public abstract class GameScene implements Updatable, Renderable, Initializable, Finalizable {

    private String nome;
    private final List sceneItens;
    private boolean loadResourcesOnInit;
    private final GameWindow gWindow;

    public GameScene(String nome) {
        this.nome = nome;
        this.sceneItens = new ArrayList();
        this.loadResourcesOnInit = true;
        this.gWindow = GameWindow.getInstance();
    }

    // -------------------------------------------------------------------------
    public void add(Object obj) {
        if (obj instanceof Renderable || obj instanceof Updatable) {
            sceneItens.add(obj);
        }
    }

    public Object get(int index) {
        return sceneItens.get(index);
    }

    public Object remove(int index) {
        return sceneItens.remove(index);
    }

    public boolean remove(Object obj) {
        return sceneItens.remove(obj);
    }

    public boolean isEmpty() {
        return sceneItens.isEmpty();
    }

    public int size() {
        return sceneItens.size();
    }

    public void clear() {
        clear(new SceneBundle());
    }

    public void clear(SceneBundle bundle) {
        for (int i = 0; i < sceneItens.size(); i++) {
            Object obj = sceneItens.get(i);
            if (obj instanceof Finalizable) {
                Finalizable fItem = (Finalizable) obj;
                fItem.end(bundle);
            }
        }
        sceneItens.clear();
    }

    public ArrayList getItems() {
        // -- Need to implement...
        return null;
    }

    public <T> List<T> getItems(Class<? extends T> clazz) {
        List<T> list = new ArrayList<>();
        Object tmp;
        for (int i = 0; i < sceneItens.size(); i++) {
            tmp = sceneItens.get(i);
            if (clazz == tmp.getClass()) {
                list.add((T) tmp);
            }
        }
        return list;
    }
    // -------------------------------------------------------------------------

    @Override
    public void init(SceneBundle bundle) {
        //stateObjectsClear();
        if (loadResourcesOnInit) {
            loadResourcesOnInit();
        }
        for (int i = 0; i < sceneItens.size(); i++) {
            Object obj = sceneItens.get(i);
            if (obj instanceof Initializable) {
                Initializable initItem = (Initializable) obj;
                initItem.init(bundle);
            }
        }
    }

    @Override
    public void end(SceneBundle bundle) {
        clear(bundle);
    }

    /**
     * @param time
     * @param totalTime
     */
    @Override
    public synchronized void update(Long time, Long totalTime) {
        Updatable tmp;
        for (int i = 0; i < sceneItens.size(); i++) {
            try {
                tmp = (Updatable) sceneItens.get(i);
                tmp.update(time, totalTime);
            } catch (Exception e) {
            }
        }
    }

    @Override
    public synchronized void render(Graphics g) {
        //widgets.render(g);
        ArrayList<Renderable> high = new ArrayList<>();
        ArrayList<Renderable> normal = new ArrayList<>();
        ArrayList<Renderable> low = new ArrayList<>();
        ArrayList<Renderable> custom = new ArrayList<>();
        Object tmp;
        Renderable rend;
        for (int i = 0; i < sceneItens.size(); i++) {
            tmp = sceneItens.get(i);
            if (tmp instanceof Renderable) {
                rend = (Renderable) tmp;
                switch (rend.getPriority()) {
                    case HIGH_PRIORITY:
                        high.add(rend);
                        break;
                    case NORMAL_PRIORITY:
                        normal.add(rend);
                        break;
                    case LOW_PRIORITY:
                        low.add(rend);
                        break;
                    default:
                        custom.add(rend);
                        break;
                }
            }
        }
        // -- Low components...
        for (int i = 0; i < low.size(); i++) {
            rend = low.get(i);
            if (rend.isVisible()) {
                rend.render(g);
            }
        }
        // -- Normal componentes...
        for (int i = 0; i < normal.size(); i++) {
            rend = normal.get(i);
            if (rend.isVisible()) {
                rend.render(g);
            }
        }
        // -- High components...
        for (int i = 0; i < high.size(); i++) {
            rend = high.get(i);
            if (rend.isVisible()) {
                rend.render(g);
            }
        }
        // -- Custom components...
        for (int i = 0; i < custom.size(); i++) {
            rend = custom.get(i);
            for (int j = custom.size(); j > i; j--) {
                Renderable rTmp = custom.get(j);
                if (rTmp.getPriority().getVal() < rend.getPriority().getVal()) {
                    custom.set(i, rTmp);
                    custom.set(j, rend);
                }
            }
        }
        for (int i = 0; i < custom.size(); i++) {
            rend = custom.get(i);
            if (rend.isVisible()) {
                rend.render(g);
            }
        }
    }

    @Override
    public Priority getPriority() {
        return Priority.NORMAL_PRIORITY;
    }

    void loadResourcesOnInit() {
        Logger.logMessage(getClass().getSimpleName() + " - loadResources(): Loading resources...");
        loadResources();
        Logger.logMessage(getClass().getSimpleName() + " - loadResources(): Load completed...");
    }

    /**
     * Resources to be loaded must be here.
     */
    public void loadResources() {
        // -- game map loading...
        List<GameMap> maps = getItems(GameMap.class);
        for (GameMap map : maps) {
            System.out.println(map);
        }
        // --
    }

    public void sleep(long milis) {
        try {
            Thread.sleep(milis);
        } catch (Exception e) {
        }
    }

    public boolean isLoadResourcesOnInit() {
        return loadResourcesOnInit;
    }

    public void setLoadResourcesOnInit(boolean loadResourcesOnInit) {
        this.loadResourcesOnInit = loadResourcesOnInit;
    }

    public String getNome() {
        return nome;
    }

    public void setNome(String nome) {
        this.nome = nome;
    }

    public GameWindow getGameWindow() {
        return gWindow;
    }

    @Override
    public boolean isVisible() {
        return true;
    }

    @Override
    public String toString() {
        return nome;
    }

}
